package org.nahtanoj.contacts;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

import com.google.gdata.client.Query;
import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactFeed;
import com.google.gdata.data.contacts.ContactGroupEntry;
import com.google.gdata.data.contacts.ContactGroupFeed;
import com.google.gdata.data.contacts.GroupMembershipInfo;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

public class MailContacts {
	private final static ContactsService cs = new ContactsService("CustomSync");
	private static final Logger log = Logger.getLogger(MailContacts.class.getName());
	private final ContactEditor ce;
	private final HashMap<String, Contact> contacts = new HashMap<String, Contact>();
	private final HashMap<String, ContactGroupEntry> groups = new HashMap<String, ContactGroupEntry>();
	private ContactGroupEntry myContacts = null;

	public MailContacts(String u) throws AuthenticationException, IOException {
		// TODO can we use oauth2
		Properties p = new Properties();
		FileInputStream in = new FileInputStream("passwords.properties");
		p.load(in);
		in.close();
		this.ce = new ContactEditor(u);
		cs.setUserCredentials(u, p.getProperty(u));
		try {
			Query myQuery = new Query(new URL("http://www.google.com/m8/feeds/groups/default/base"));
			myQuery.setMaxResults(1000);
			ContactGroupFeed resultFeed = cs.query(myQuery, ContactGroupFeed.class);
			for (ContactGroupEntry groupEntry : resultFeed.getEntries()) {
//				log.info(groupEntry.getSelfLink().getHref() + " " + groupEntry.getTitle().getPlainText());
				if (!groupEntry.getTitle().getPlainText().startsWith("System Group: My Contacts")) {
					this.groups.put(groupEntry.getSelfLink().getHref(), groupEntry);
				} else {
					this.myContacts = groupEntry;
				}
			}

			myQuery = new Query(new URL("https://www.google.com/m8/feeds/contacts/default/full"));
			myQuery.setMaxResults(1000);
			ContactFeed contactFeed = cs.query(myQuery, ContactFeed.class);
			for (ContactEntry entry : contactFeed.getEntries()) {
				for (GroupMembershipInfo group : entry.getGroupMembershipInfos()) {
					// TODO this looks very similar and could be reduced
					if (this.groups.get(group.getHref()) != null) {
						Contact c = new Contact(this.groups.get(group.getHref()), entry);
						c.setSystemContactGroupEntry(myContacts);
						if (contacts.get(c.hashCalc()) != null) {
							log.warning("Duplicate mail hash:" + c.getLastName() + " " + c.getFirstName());
							contacts.put(c.hashCalc() + System.currentTimeMillis(), c);
						} else {
							contacts.put(c.hashCalc(), c);
						}
					} else {
						// We only add if contact is only in System Group i.e. not assigned a group yet
						// TODO can we do this a better way than chasing null in groups
						// TODO what about adding SystemGroup to groups and testing for only it (size=1)
						// Starred contacts in mail might also need filtering out
						if (entry.getGroupMembershipInfos().size() == 1) {
							log.info("No group:" + entry.getName().getFullName().getValue());
							// Only no group if no other group
							Contact c = new Contact(this.groups.get(group.getHref()), entry);
							c.setSystemContactGroupEntry(myContacts);
							// TODO everything is in MyContacts by default so make sure we don't add twice
							// well it doesn't because it's not in if only in 1 group
//							if (c.getHash() == null || c.getHash().length() == 0) {
								if (contacts.get(c.hashCalc()) != null) {
									log.warning("Duplicate mail hash:" + c.getLastName() + " " + c.getFirstName());
									contacts.put(c.hashCalc() + System.currentTimeMillis(), c);
								} else {
									contacts.put(c.hashCalc(), c);
								}
//							} else {
//								Trying to work out how to add Carlsen when he's only in system groups
	//							contacts.put(c.getContact().hashCalc(), c); - we must not add here since it should already have a group
								// TODO make sure that when we insert mail or update the hash on mail it must have a group
//							}
						}
					}
				}
			}
			log.info("" + contacts.size());
		} catch (IOException e) {
			log.warning("IOException:" + e.getMessage());
		} catch (ServiceException e) {
			log.warning("ServiceException:" + e.getMessage());
		}
	}


	// TODO this needs to validate the contact and return a fault which can go in the spreadsheet
	// TODO why are we putting hash here but last first elsewhere?
//	@Override
//	public void addContactaSEETODOBELOW(Contact c) throws AuthenticationException {
//		// Don't insert if there are validation comments present
//		if (c.getValidation() == null || c.getValidation().length() == 0) {
//			String groupHref = getGroup(c.getCategory());
//			c.setContactGroupEntry(this.groups.get(groupHref));
//			c.setSystemContactGroupEntry(myContacts);
//			c.updateContactEntry();
//			if (contacts.get(c.getHash()) != null) {
//				log.warning("Duplicate mail hash:" + c.getLastName() + " " + c.getFirstName());
//			}
//			// TODO should this be c.calchash instead
//			contacts.put(c.getHash(), c);
//		}
//	}

//	private String addGroup(String category) throws AuthenticationException {
//		ContactGroupEntry cg = this.ce.createGroup(category);
//		this.groups.put(cg.getSelfLink().getHref(), cg);
//		return cg.getSelfLink().getHref();
//	}

	public boolean contains(Contact d) {
		if (this.contacts.containsKey(d.getHash())) {
			return true;
		}
		List<Contact>matches = new ArrayList<Contact>();
		for (String hash : this.contacts.keySet()) {
			Contact m = this.contacts.get(hash);
			String mLast = m.getLastName(); String mFirst = m.getFirstName();
			String dLast = d.getLastName(); String dFirst = d.getFirstName();
			if (mLast == null) {
				mLast = "";
			}
			if (mFirst == null) {
				mFirst = "";
			}
			if (dLast == null) {
				dLast = "";
			}
			if (dFirst == null) {
				dFirst = "";
			}
			if (mLast.equals(dLast) && mFirst.equals(dFirst)) {
				matches.add(m);
			}
		}
		if (matches.size() == 1) {
			log.info("Matching by name:" + d.getLastName() + " " + d.getFirstName() + " " + matches.get(0).getHash());
			return true;
		}
		for (Contact c : matches) {
			log.warning("Multiple match:" + c.getLastName() + " " + c.getFirstName());
		}
		return false;
	}

	public HashMap<String, Contact> getContacts() {
		return this.contacts;
	}

	// If category does not exist create it and add to groups
	// Return href of group
//	private String getGroup(String category) throws AuthenticationException {
//		String href = null;
//		for (ContactGroupEntry g : this.groups.values()) {
//			if (g.getTitle().getPlainText().equals(category) ||
//					g.getTitle().getPlainText().equals("System Group: " + category)) {
//				href = g.getSelfLink().getHref();
//			}
//		}
//		if (href == null) {
//			href = addGroup(category);
//		}
//		return href;
//	}


	public void hashUpdate(Contact contact) {
		contact.hashUpdate();
		contact.updateContactEntry();
		this.ce.updateContact(contact.getContactEntry());
	}


//	public void saveContact(ContactEntry contactEntry) {
//		this.ce.updateContact(contactEntry);
//	}
}
